package file.deviations 
{
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.IBitmapDrawable;
	import flash.display.MovieClip;
	import flash.display.PixelSnapping;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	/**
	 * Data Mix
	 * 'Mi' as micro
	 * 'x' as factors that influence has been incalculable.
	 * 'Mix' as mix together different elements!
	 * @exception	All change must be from root project "package file.deviations"
	 * @since 	    11/10/2011 11:34 AM
	 * @example     http://hungconcon.blogspot.com/2011/11/movieclip-to-animal-bitmap-101.html
	 * @see		    http://code.google.com/p/hutra/
	 * @see         svn http://code.google.com/p/hutra/
	 * @see         wiki http://wiki.hutrasoft.com/2011/11/datamix.html
	 * @author 	    hungconcon
	 * @version     1.0.0.4
	 */
	public class DataMix 
	{
		protected var w:Number 		=-1;
		protected var h:Number		=-1;
		protected var d:Array 		= null;
		protected var b:BitmapData 	= null;
		protected var l:Number 		= 0;
		
		/**
		 * In special cases, we only get the size, no need to get data
		 * @param	mc
		 * @param	isdata if true get all including data else false only get size
		 */
		public function DataMix(mc:MovieClip,isdata:Boolean=true) {
			setup(mc,isdata);
		}
		public function get width():Number { 
			return this.w;
		}
		public function get height():Number { 
			return this.h;
		}
		public function get length():Number { 
			return this.l;
		}
		public function get data():Array { 
			return this.d;
		}
		/**
		 * Return value bitmapData that is parse from data.
		 */
		public function get bitmap():BitmapData {
			//[1]
			if (b != null) return b;
			//[2]
			var sizeW:int 		= this.w;
			var sizeH:int 		= this.h;
			var length:Number 	= this.d.length;
			var rowcol:Number 	= Math.sqrt(length);
			var row:Number 		= Math.floor(rowcol);
			var col:Number 		= Math.ceil(rowcol); 
			var tx:Number  		= 0;
			var ty:Number  		= 0;
			b = new BitmapData(col * sizeW, row * sizeH, true, 0x00FFFFFF);
			for (var i:int = 0; i < length; i++ ) {
				tx = ( i % col ) * sizeW;
				ty =  Math.floor( i / col ) * sizeH;
				b.draw(this.d[i], new Matrix(1, 0, 0, 1, tx, ty));
			}
			return b;
		}
		/**
		 * @param	mc Make sure you seted the movie correctly!
		 */
		private function setup(mc:MovieClip,isdata:Boolean):void {
			var recBase:Rectangle   = getRecBase(mc);
			if ((recBase.width <= 1) || (recBase.height <= 1)) {
				d = new Array();
				d.push(new BitmapData(1, 1, true, 0x00000000));
				w = 1;
				h = 1;
				return;
			}
			var listBase:Array 	    = getListBase(mc, recBase);
			var recActual:Rectangle = getRecActual(listBase, recBase);			
			var listFace:Array		= getListActual(listBase, recActual);
			var recTrim:Rectangle	= getTrimBitmapData(listFace);
			
			w = recTrim.width;
			h = recTrim.height;
			
			if (isdata) {
				clearListBase(listBase); // necessary!
				d = getListActual(listFace, recTrim);
				l = d.length;
			}else {
				clearListBase(listBase); // necessary!
				clearListBase(listFace); // necessary!
				d = null;
			}
		}
		/**
		 * The function trim a bit transparent in the Bitmapdata
		 * @param	a list BitmapData actual, but non-trim
		 * @return rectangle 
		 */
		private function getTrimBitmapData(a:Array):Rectangle {
			var maxx:Number = NaN;
			var maxy:Number = NaN;
			var minx:Number = NaN;
			var miny:Number = NaN;
			var maxw:Number = NaN;
			var maxh:Number = NaN;
			var temp:Number = NaN;
			for (var i:int = 0 ; i < a.length; i++){
				var rc:Rectangle = a[i].getColorBoundsRect(0xFFFFFFFF, 0x000000, false);
				if (rc.width!=0 && rc.height!=0) { //rc.x !=0 && rc.y!=0 &&
					minx = ((temp = rc.x) < minx)?temp:(isNaN(minx))?temp:minx;
					miny = ((temp = rc.y) < miny)?temp:(isNaN(miny))?temp:miny;
					maxw = ((temp = rc.width) > maxw)?temp:(isNaN(maxw))?temp:maxw;
					maxh = ((temp = rc.height) > maxh)?temp:(isNaN(maxh))?temp:maxh;
					maxx = ((temp = rc.x + rc.width) > maxx)?temp:(isNaN(maxx))?temp:maxx;
					maxy = ((temp = rc.y + rc.height) > maxy)?temp:(isNaN(maxy))?temp:maxy;
				}else {
					// I don't know!
				}
			}
			var maxwi:Number = maxx - minx;
			var maxhi:Number = maxy - miny;			
			return new Rectangle(minx, miny, maxwi, maxhi);
		}
		
		
		/**
		 * Determine the total area and the initial coordinates of the object.
		 * [1]
		 * Find the smallest value x
		 * Find the smallest value of y
		 * Find the largest value of x
		 * Find the largest value of y
		 * [2]
		 * Find the largest value of width
		 * Find the largest value of hight
		 * [3]
		 * draw an object allows you to define regions of the object
		 * we will remove it when we get the full picture
		 * [4]
		 * Specify the width and height from [1],[2]
		 * @param	mc
		 * @return Rectangle
		 */
		private function getRecBase(mc:MovieClip):Rectangle {
			var totalframe:int = mc.totalFrames;
			var maxx:Number = NaN;
			var maxy:Number = NaN;
			var minx:Number = NaN;
			var miny:Number = NaN;
			var maxw:Number = NaN;
			var maxh:Number = NaN;
			var temp:Number = NaN;
			//[1]
			for (var i:int = 1; i <= totalframe; i++) {
				mc.gotoAndStop(i);
				var rc:Rectangle = mc.getBounds(mc);
				
				minx = ((temp = rc.x) < minx)?temp:(isNaN(minx))?temp:minx;
				miny = ((temp = rc.y) < miny)?temp:(isNaN(miny))?temp:miny;
				maxw = ((temp = rc.width) > maxw)?temp:(isNaN(maxw))?temp:maxw;
				maxh = ((temp = rc.height) > maxh)?temp:(isNaN(maxh))?temp:maxh;
				
				maxx = ((temp = rc.x + rc.width) > maxx)?temp:(isNaN(maxx))?temp:maxx;
				maxy = ((temp = rc.y + rc.height) > maxy)?temp:(isNaN(maxy))?temp:maxy;
			}
			//[2]
			var maxwi:Number = maxx - minx;
			var maxhi:Number = maxy - miny;
			//[3]
			var pon:Sprite = new Sprite();
				pon.graphics.beginFill(0x999999, 0);
				pon.graphics.drawRect(minx, miny, maxwi, maxhi);
				pon.graphics.endFill();
				mc.addChild(pon);
			//[4]	
			var wi:Number = dePercent(mc.scaleX * 100, 0, maxwi);
			var hi:Number = dePercent(mc.scaleX * 100, 0, maxhi);
			return new Rectangle(minx, miny, fixNumber(wi),fixNumber(hi));
		}
		private function fixNumber(numberValue:Number):Number
		{
			return isNaN(Number(numberValue)) ? 0 : Math.round(Number(numberValue) * 100000) / 100000;
		}
		/**
		 * Retrieving images based on the basic rectangle
		 * @param	mc
		 * @param	rec
		 * @return
		 */
		private function getListBase(mc:MovieClip, rec:Rectangle):Array {
			var totalframe:int = mc.totalFrames;
			var list:Array = new Array();			
			for (var i:int = 1; i <= totalframe; i++) {
				mc.gotoAndStop(i);
				list.push(getBaseBitmapData(mc,rec,true ));
			}			
			return list;
		}
		/**
		 * Find a new Rectangle not including the superfluous
		 * @param	list
		 * @param	rec
		 * @return
		 */
		private function getRecActual(list:Array,rec:Rectangle):Rectangle {
			var minx:Number = NaN;
			var miny:Number = NaN;
			var temp:Number = NaN;
			var data:BitmapData;
			var vbt:Rectangle;
			for (var i:int = 0; i <list.length; i++) {
				data = list[i] as BitmapData;
				vbt = getViboty(data, rec);
				minx = ((temp = vbt.x) < minx)?temp:(isNaN(minx))?temp:minx;
				miny = ((temp = vbt.y) < miny)?temp:(isNaN(miny))?temp:miny;
			}
			return new Rectangle(minx, miny, rec.width - minx, rec.height - miny);
		}
		/**
		 * Get visible bounds of transparent 
		 * [1] Frees memory that is used to store the BitmapData object.
		 * @param	mc
		 * @param	rec
		 * @return
		 */
		private function getViboty(mc:BitmapData,rec:Rectangle): Rectangle {
			var data:BitmapData = new BitmapData(rec.width, rec.height, true, 0x00000000);
				data.draw(mc);
			var trec:Rectangle = data.getColorBoundsRect(0xFFFFFFFF, 0x000000, false);
				data.dispose();	//[1]
			  return trec;
		}
		/**
		 * [1] Frees memory that is used to store the BitmapData object.
		 * @param	list
		 * @param	newrec
		 * @return
		 */
		private function getListActual(list:Array, newrec:Rectangle):Array {
			var data:BitmapData;
			var newlist:Array = new Array();
			for (var i:int = 0; i < list.length; i++) {
				data = list[i] as BitmapData;
				newlist.push(getActutalBitmapData(data, newrec));
			}
			clearListBase(list); //[1]
			return newlist;
		}
		/**
		 * Frees memory!
		 * @param	the arraylist
		 */
		private function clearListBase(a:Array):void {
			if (a == null) return;
			for (var i:int = 0; i < a.length; i++) delete a.splice(i, 1);
			if(a!=null) if (a.length > 0) clearListBase(a);
			a = null;
		}
		/**
		 * Move
		 * Center point(0,0)
		 * |----------------|
		 * |	0.0			|
		 * |				|
		 * |		.---------------.
		 * |		|(80,80,100,90)	|
		 * |		|	Object		|
		 * |		|				|
		 * |		.---------------.
		 * |----------------|
		 * 
		 * to
		 * 
		 * .0.0------------.|
		 * |			   ||
		 * |	Object	   ||
		 * |			   ||
		 * .---------------.|
		 * |----------------|
		 * 
		 * @param	a
		 * @param	b
		 * @return
		 */
		private function movePosition(ax:Number, bx:Number):Number {
			// -25 0 5 =  30 [1]
			// -25 0 -5 = 20 [2]
			//  5  0 25 = 20 [3]
			if (ax > 0 && bx > 0) return bx - ax;						//[3]
			if (ax < 0 && bx < 0) return Math.abs(ax) - Math.abs(bx); 	//[2]
			if (ax < 0 && bx > 0) return Math.abs(ax) + bx; 			//[1]
			return 0;
		}
		/**
		 * [1],[2],[3] Scaled
		 * @param	mc
		 * @param	rec
		 * @param	smoothing
		 * @return
		 */
		private function getBaseBitmapData(mc:MovieClip, rec:Rectangle, smoothing:Boolean ): BitmapData {
			//[1]
			var bound:Rectangle = mc.getBounds(mc);
			var matrix:Matrix = new Matrix(1, 0, 0, 1, -bound.x, -bound.y);
				matrix.scale(mc.scaleX, mc.scaleX);
			//[2]
			var wi:Number = dePercent(mc.scaleX * 100, 0, bound.width);		//[2]
			var hi:Number = dePercent(mc.scaleX * 100, 0, bound.height);	//[3]
			//[3]
			var bitmapdata:BitmapData = new BitmapData( rec.width, rec.height, true, 0x00FFFFFF );		
				bitmapdata.draw(mc, matrix, null, null, null,smoothing);
			//[4]
			var xpo:Number = movePosition(rec.x, bound.x);
			var ypo:Number = movePosition(rec.y, bound.y);
			//[5]	
			var newbitdata:BitmapData = new BitmapData( rec.width, rec.height, true, 0x00FFFFFF );
				newbitdata.copyPixels(bitmapdata, new Rectangle(0, 0, rec.width, rec.height), new Point(xpo, ypo));
			//[6]	
				bitmapdata.dispose();
				
			return newbitdata;
		}
		/**
		 * Function on lib: Hutrasoft.com...NumberUtilities.as.dePercent()
		 * Percentage change from the current number
		 * @param	percent
		 * @param	start
		 * @param	end
		 * @return
		 */
		private function dePercent(p:Number, s:Number = 0, e:Number = 100):Number {
			return (e - ((e - s) - ((p/100) * (e - s))));
		}
		/**
		 * [1] Frees memory that is used to store the BitmapData object.
		 * @param	bitmapdata
		 * @param	rec
		 * @return
		 */
		private function getActutalBitmapData(bitmapdata:BitmapData, rec:Rectangle): BitmapData {
			var newbitdata:BitmapData = new BitmapData( rec.width, rec.height, true,0x00FFFFFF);
				newbitdata.copyPixels(bitmapdata, new Rectangle(rec.x, rec.y, rec.width, rec.height), new Point(0,0));
				bitmapdata.dispose(); //[1]
				return newbitdata;
		}
		
	}
}